#include "../rdx/rdx_pragmas.hpp"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <direct.h>
#include <time.h>

#include "../lua/src/lua.h"
#include "../rdx/rdx_basictypes.hpp"

#define BACKSTACK_SIZE	(sizeof(HugeInt)*3+2)

using namespace RDX;

int closeCacheFile(lua_State *L);
int writeCacheObject(lua_State *L);
int readCacheObject(lua_State *L);
int createCacheFile(lua_State *L);
Int64 MSecTime();

template<class LargeT, class SmallT>
inline LargeT SVMin()
{
	SmallT sv = (-1) << (sizeof(SmallT) * 8 - 1);
	return static_cast<LargeT>(sv);
}

template<class LargeT, class SmallT>
inline LargeT SVMax()
{
	SmallT sv = ~((-1) << (sizeof(SmallT) * 8 - 1));
	return static_cast<LargeT>(sv);
}


UInt32 ShiftRightRotate(UInt32 v, int count)
{
	return (v >> count) | (v << (32 - count));
}


void SHA256Cycle(UInt32 data[8], UInt32 seed, int round)
{
	static const UInt32 k[] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
		0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
		0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
		0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
		0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
		0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
		0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
		0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };

	UInt32 ch = (data[4] & data[5]) ^ ((~data[4]) & data[6]);
	UInt32 s1 = ShiftRightRotate(data[4], 6) ^ ShiftRightRotate(data[4], 11) ^ ShiftRightRotate(data[4], 25);
	UInt32 combine1 = k[round] + seed + ch + data[7] + s1;
	UInt32 ma = (data[0] & data[1]) ^ (data[0] & data[2]) ^ (data[1] & data[2]);
	UInt32 s0 = ShiftRightRotate(data[0], 2) ^ ShiftRightRotate(data[0], 13) ^ ShiftRightRotate(data[0], 22);
	data[7] = data[6];
	data[6] = data[5];
	data[5] = data[4];
	data[4] = data[3] + combine1;
	data[3] = data[2];
	data[2] = data[1];
	data[1] = data[0];
	data[0] = combine1 + ma + s0;
}

void SHA256(UInt32 data[8], const void *ptr, LargeUInt n)
{
	const UInt8 *cptr = (const UInt8 *)ptr;
	int i;

	data[0] = 0x6a09e667;
	data[1] = 0xbb67ae85;
	data[2] = 0x3c6ef372;
	data[3] = 0xa54ff53a;
	data[4] = 0x510e527f;
	data[5] = 0x9b05688c;
	data[6] = 0x1f83d9ab;
	data[7] = 0x5be0cd19;

	while(n > 0)
	{
		UInt32 seeds[64];

		if(n >= 64)
		{
			for(i=0;i<64;i+=4)
			{
				seeds[i/4] = cptr[0] | (cptr[1] << 8) | (cptr[2] << 16) | (cptr[3] << 24);
				cptr += 4;
			}
			n -= 64;
		}
		else
		{
			for(i=0;i<64;i+=4)
				seeds[i/4] = cptr[i%n] | (cptr[(i+1)%n] << 8) | (cptr[(i+2)%n] << 16) | (cptr[(i+3)%n] << 24);
			n = 0;
		}

		for(i=16;i<64;i++)
		{
			UInt32 s0 = (ShiftRightRotate(seeds[i-15], 7) ^ ShiftRightRotate(seeds[i-15], 18) ^ ShiftRightRotate(seeds[i-15], 3));
			UInt32 s1 = (ShiftRightRotate(seeds[i-2], 17) ^ ShiftRightRotate(seeds[i-2], 19) ^ ShiftRightRotate(seeds[i-2], 10));
			seeds[i] = seeds[i-16] + seeds[i-7] + s0 + s1;
		}

		for(i=0;i<64;i++)
			SHA256Cycle(data, seeds[i], i);
	}
}

void CRC32(UInt32 *outCRC, const void *ptr, LargeUInt n)
{
	static const UInt32 crctable[] =
	{
		0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
		0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
		0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
		0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
		0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
		0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
		0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
		0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
		0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
		0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
		0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
		0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
		0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
		0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
		0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
		0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
		0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
		0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
		0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
		0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
		0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
		0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
		0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
		0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
		0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
		0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
		0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
		0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
		0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
		0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
		0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
		0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
	};

	const UInt8 *bytes = static_cast<const UInt8 *>(ptr);
	UInt32 crc = 0xffffffff;	// ~previous crc

	while(n--)
		crc = crctable[(crc ^ (*bytes++)) & 0xff] ^ (crc >> 8);
	*outCRC = ~crc;
}


int myatoll(const char *str, HugeInt &out)
{
	const char *baseStr = str;

	bool sign = false;
	if(str[0] == '-')
	{
		sign = true;
		str++;
	}
	HugeInt rv = 0;
	while(str[0] >= '0' && str[0] <= '9')
	{
		if(sign)
			rv = rv * HugeInt(10) - static_cast<HugeInt>(str[0] - '0');
		else
			rv = rv * HugeInt(10) + static_cast<HugeInt>(str[0] - '0');
		str++;
	}
	out = rv;
	return str - baseStr;
}

int IntToString(char *out, HugeInt v)
{
	char backstack[BACKSTACK_SIZE];
	int digits = 0;
	int chars = 0;
	bool negative = false;

	if(v < 0)
	{
		*out++ = '-';
		chars++;
		negative = true;
	}

	do
	{
		if(negative)
			backstack[digits++] = static_cast<char>(-(v % HugeInt(10)) + '0');
		else
			backstack[digits++] = static_cast<char>((v % HugeInt(10)) + '0');
		v = v / HugeInt(10);
	} while(v);

	while(digits)
	{
		*out++ = backstack[--digits];
		chars++;
	}

	*out = '\0';

	return chars;
}

int UIntToString(char *out, HugeUInt v)
{
	char backstack[BACKSTACK_SIZE];
	int digits = 0;
	int chars = 0;

	do
	{
		backstack[digits++] = static_cast<char>((v % HugeUInt(10)) + '0');
		v = v / HugeUInt(10);
	} while(v);

	while(digits)
	{
		*out++ = backstack[--digits];
		chars++;
	}

	*out = '\0';

	return chars;
}

void DecomposedToString(char *out, Int64 frac, Int32 x)
{
	out += IntToString(out, frac);
	if(x)
	{
		*out++ = '^';
		out += IntToString(out, x);
	}
	*out = '\0';
}


void DecomposeString(const char *str, Int64 &frac, Int32 &x)
{
	str += myatoll(str, frac);

	if(*str == '^')
	{
		str++;
		HugeInt v;
		myatoll(str, v);
		x = static_cast<Int32>(v);
	}
	else
		x = 0;
}

void DecomposeFloat(float f, Int32 &frac, Int32 &x)
{
	union
	{
		float f;
		Int32 i;
	} RDX_MAY_ALIAS u;

	u.f = f;
	if(u.i == 0)
	{
		frac = 0;
		x = 0;
		return;
	}

	Int32 baseFrac = u.i & 0x7FFFFF;
	baseFrac |= 0x800000;
	Int32 baseX = (u.i & 0x7F800000) >> 23;

	while(!(baseFrac & 1))
	{
		baseX++;
		baseFrac >>= 1;
	}

	x = baseX - 150;

	if((u.i & 0x80000000) != 0)
		baseFrac = -baseFrac;
	frac = baseFrac;
}

float RecomposeFloat(Int32 frac, Int32 x)
{
	union
	{
		float f;
		Int32 i;
	} RDX_MAY_ALIAS u;

	if(frac == 0)
		return 0;

	bool sign = false;
	if(frac < 0)
	{
		sign = true;
		frac = -frac;
	}

	x += 150;

	while(!(frac & 0x800000))
	{
		x--;
		frac <<= 1;
	}

	frac &= 0x7FFFFF;
	u.i = frac | (x << 23);
	if(sign)
		u.i |= 0x80000000;

	return u.f;
}

void DecomposeDouble(double f, Int64 &frac, Int32 &x)
{
	union
	{
		double f;
		Int64 i;
	} RDX_MAY_ALIAS u;

	u.f = f;
	if(u.i == 0)
	{
		frac = 0;
		x = 0;
		return;
	}

	Int64 baseFrac = u.i & 0xFFFFFFFFFFFFFLL;
	baseFrac |= 0x10000000000000LL;
	long baseX = static_cast<Int32>( (u.i & 0x7FF0000000000000LL) >> 52LL);

	while(!(baseFrac & 1))
	{
		baseX++;
		baseFrac >>= 1LL;
	}

	x = baseX - 1075;

	if((u.i & 0x8000000000000000LL) != 0)
		baseFrac = -baseFrac;
	frac = baseFrac;
}


double RecomposeDouble(Int64 frac, Int32 x)
{
	union
	{
		double f;
		Int64 i;
	} RDX_MAY_ALIAS u;

	x += 1075;
	
	bool sign = false;
	if(frac < 0LL)
	{
		sign = true;
		frac = -frac;
	}

	while(!(frac & 0x10000000000000LL))
	{
		x--;
		frac <<= 1LL;
	}

	frac &= 0xFFFFFFFFFFFFFLL;
	u.i = frac | (static_cast<Int64>(x) << 52LL);
	if(sign)
		u.i |= 0x8000000000000000LL;

	return u.f;
}



void RecomposeVariant(Int64 frac, Int32 x, float &out)
{
	out = RecomposeFloat(static_cast<Int32>(frac), x);
}

void RecomposeVariant(Int64 frac, Int32 x, double &out)
{
	out = RecomposeDouble(frac, x);
}

// StrToNumber template overloads
void StrToNumber(const char *str, double &out)
{
	Int64 frac;
	Int32 x;
	DecomposeString(str, frac, x);
	out = RecomposeDouble(frac, x);
}

void StrToNumber(const char *str, float &out)
{
	Int64 frac;
	Int32 x;
	DecomposeString(str, frac, x);
	out = RecomposeFloat(static_cast<Int32>(frac), x);
}

void StrToNumber(const char *str, HugeInt &out)
{
	myatoll(str, out);
}

void StrToNumber(const char *str, Int32 &out)
{
	HugeInt temp;
	myatoll(str, temp);
	out = static_cast<Int32>(temp);
}

void StrToNumber(const char *str, Int16 &out)
{
	HugeInt temp;
	myatoll(str, temp);
	out = static_cast<Int16>(temp);
}

void StrToNumber(const char *str, Int8 &out)
{
	HugeInt temp;
	myatoll(str, temp);
	out = static_cast<Int8>(temp);
}

void StrToNumber(const char *str, UInt64 &out)
{
	HugeInt temp;
	myatoll(str, temp);
	out = static_cast<UInt64>(temp);
}

void StrToNumber(const char *str, UInt32 &out)
{
	HugeInt temp;
	myatoll(str, temp);
	out = static_cast<UInt32>(temp);
}

void StrToNumber(const char *str, UInt16 &out)
{
	HugeInt temp;
	myatoll(str, temp);
	out = static_cast<UInt16>(temp);
}

void StrToNumber(const char *str, UInt8 &out)
{
	HugeInt temp;
	myatoll(str, temp);
	out = static_cast<UInt8>(temp);
}

void StrToNumber(const char *str, bool &out)
{
	out = (strcmp(str, "false") != 0);
}

// NumberToStr overloads
void NumberToStr(double v, char *out)
{
	Int64 frac;
	Int32 x;
	DecomposeDouble(v, frac, x);
	DecomposedToString(out, frac, x);
}

void NumberToStr(float v, char *out)
{
	Int32 frac;
	Int32 x;
	DecomposeFloat(v, frac, x);
	DecomposedToString(out, frac, x);
}

void NumberToStr(Int64 v, char *out)
{
	IntToString(out, v);
}

void NumberToStr(Int32 v, char *out)
{
	IntToString(out, v);
}

void NumberToStr(Int16 v, char *out)
{
	IntToString(out, v);
}

void NumberToStr(Int8 v, char *out)
{
	IntToString(out, v);
}

void NumberToStr(UInt64 v, char *out)
{
	UIntToString(out, v);
}

void NumberToStr(UInt32 v, char *out)
{
	UIntToString(out, v);
}

void NumberToStr(UInt16 v, char *out)
{
	UIntToString(out, v);
}

void NumberToStr(UInt8 v, char *out)
{
	UIntToString(out, v);
}

void NumberToStr(bool v, char *out)
{
	strcpy(out, v ? "true" : "false");
}

void HexStringifyLong(UInt32 v, char *cout)
{
	static const char *nibbles = "0123456789abcdef";
	int i;
	for(i=0;i<8;i++)
		cout[i] = nibbles[ (v >> (i*4)) & 0xf ];
}


int sha256(lua_State *L)
{
	size_t len;
	const char *str = lua_tolstring(L, 1, &len);
	UInt32 data[8];
	char output[64];

	SHA256(data, str, len);

	for(int i=0;i<8;i++)
		HexStringifyLong(data[i], output + i*8);

	lua_pushlstring(L, output, 64);
	return 1;
}

int crc32(lua_State *L)
{
	size_t len;
	const char *str = lua_tolstring(L, 1, &len);
	char output[8];
	UInt32 crc;
	CRC32(&crc, str, len);
	HexStringifyLong(crc, output);
	lua_pushlstring(L, output, 8);
	return 1;
}


int parseNumber(lua_State *L)
{
	const char *str = lua_tostring(L, 1);
	const char *subStr = str;
	bool containsDecimal = false;
	bool endsWithF = false;
	bool containsCarat = false;
	char numericOutput[50];
	char rawNumber[201];

	if(!str || strlen(str) >= 200)
		return 0;

	LargeUInt nChars = strlen(str);
	strcpy(rawNumber, str);

	// See if this contains a decimal
	while(*subStr)
	{
		if(subStr[0] == '.')
			containsDecimal = true;
		else if(subStr[0] == '^')
			containsCarat = true;
		subStr++;
	}

	endsWithF = (str[0] != '\0' && subStr[-1] == 'f');

	if(containsDecimal || containsCarat || endsWithF)
	{
		if(endsWithF)
		{
			nChars--;
			rawNumber[nChars] = '\0';
		}

		if(containsCarat)
		{
			// Just recycle it
			lua_pushstring(L, rawNumber);
			if(endsWithF)
				lua_pushstring(L, "Core.float");
			else
				lua_pushstring(L, "Core.double");
			return 2;
		}
		else if(endsWithF)
		{
			Int32 frac, x;
		
			double d = atof(rawNumber);
			DecomposeFloat(static_cast<float>(d), frac, x);

			DecomposedToString(numericOutput, frac, x);
			lua_pushstring(L, numericOutput);
			lua_pushstring(L, "Core.float");
			return 2;
		}
		else
		{
			Int64 frac;
			Int32 x;
		
			double d = atof(rawNumber);
			DecomposeDouble(d, frac, x);

			DecomposedToString(numericOutput, frac, x);
			lua_pushstring(L, numericOutput);
			lua_pushstring(L, "Core.double");
			return 2;
		}
	}

	// Integer
	Int64 i;
	myatoll(str, i);

	IntToString(numericOutput, i);
	lua_pushstring(L, numericOutput);

	if(i < SVMin<Long, Int>() || i > SVMax<Long, Int>())
		lua_pushstring(L, "Core.long");
	else if(i < SVMin<Long, Short>() || i > SVMax<Long, Short>())
		lua_pushstring(L, "Core.int");
	else
		lua_pushstring(L, "Core.int");	// TODO: Maybe vary this

	return 2;
}

int cf_coerce_noop(lua_State *L)
{
	lua_pushvalue(L, 1);
	return 1;
}

#define BIN_OP(opName, op)	\
	template<class _T, class _Tout>\
	struct BinOp##opName\
	{\
		typedef _T InType;\
		typedef _Tout OutType;\
		inline static bool Operate(_T left, _T right, bool zeroCheck, _Tout &out)\
		{\
			if(zeroCheck && right == static_cast<_T>(0))\
				return false;\
			out = static_cast<_Tout>(left op right);\
			return true;\
		}\
	}

#define UN_OP(opName, op)	\
	template<class _T>\
	struct UnOp##opName\
	{\
		typedef _T OpType;\
		inline static bool Operate(_T v, _T &out)\
		{\
			out = op v;\
			return true;\
		}\
	}

BIN_OP(Add, +);
BIN_OP(Subtract, -);
BIN_OP(Multiply, *);
BIN_OP(Divide, /);
BIN_OP(Modulo, %);
BIN_OP(GE, >=);
BIN_OP(GT, >);
BIN_OP(LE, <=);
BIN_OP(LT, <);
BIN_OP(EQ, ==);
BIN_OP(NE, !=);
UN_OP(Negate, -);


template<class _TbinOp, bool zeroCheck>
int ConstFoldBinaryOp(lua_State *L)
{
	_TbinOp::InType left;
	_TbinOp::InType right;
	_TbinOp::OutType out;
	StrToNumber(lua_tostring(L, 1), left);
	StrToNumber(lua_tostring(L, 2), right);
	bool succeeded = _TbinOp::Operate(left, right, zeroCheck, out);
	if(!succeeded)
		lua_pushnil(L);
	else
	{
		char number[100];
		NumberToStr(out, number);
		lua_pushstring(L, number);
	}
	return 1;
}

template<class _TsourceType, class _TdestType>
int ConstFoldConvertOp(lua_State *L)
{
	_TsourceType v;
	StrToNumber(lua_tostring(L, 1), v);

	char number[100];
	NumberToStr(static_cast<_TdestType>(v), number);
	lua_pushstring(L, number);
	return 1;
}

template<class _TsourceType>
int IntToStringOp(lua_State *L)
{
	_TsourceType v;
	StrToNumber(lua_tostring(L, 1), v);

	char number[RDX_MAX_ENCODED_NUMBER_SIZE];
	NumberToStr(v, number);
	lua_pushstring(L, number);
	return 1;
}

template<class _TunOp>
int ConstFoldUnaryOp(lua_State *L)
{
	_TunOp::OpType v;
	_TunOp::OpType out;
	StrToNumber(lua_tostring(L, 1), v);
	bool succeeded = _TunOp::Operate(v, out);
	if(!succeeded)
		lua_pushnil(L);
	else
	{
		char number[100];
		NumberToStr(out, number);
		lua_pushstring(L, number);
	}
	return 1;
}

template<class Tf>
int encodeFloat(const char *coreType, lua_State *L)
{
	Int64 frac;
	Int32 x;

	DecomposeString(lua_tostring(L, 2), frac, x);

	Tf fval;
	RecomposeVariant(frac, x, fval);
	
	union { Tf f; Int64 i64; Int32 i32; } RDX_MAY_ALIAS u;
	u.i32 = 0;
	u.i64 = 0;
	u.f = fval;

	char str[BACKSTACK_SIZE];

	if(sizeof(Tf) == sizeof(Float32))
		IntToString(str, u.i32);
	else if(sizeof(Tf) == sizeof(Float64))
		IntToString(str, u.i64);
	else
	{
		lua_pushstring(L, "constant");
		lua_pushnil(L);
		lua_pushnil(L);
		lua_pushstring(L, coreType);
		lua_pushvalue(L, 2);
		return 5;
	}

	// Push the converted value
	lua_pushstring(L, "constant");
	lua_pushstring(L, str);
	lua_pushnil(L);
	lua_pushstring(L, coreType);
	lua_pushnil(L);
	return 5;
}


int encodeBytecodeInteger(lua_State *L)
{
	char numberSequence[sizeof(HugeInt)*12+1];
	numberSequence[0] = '\0';

	Int64 parsed;
	myatoll(lua_tostring(L, 1), parsed);
	Int64 stopValue;

	if(parsed < 0)
		stopValue = -1;
	else
		stopValue = 0;

	int numPieces = 0;
	Int64 stopTest = parsed;
	do
	{
		stopTest = stopTest >> 7;
		numPieces++;
	} while(stopTest != stopValue);

	// The final value is sign extended, so make sure the last bit will extend to the correct value
	if(parsed < 0 && (parsed & (static_cast<Int64>(1) << (numPieces * 7 - 1))) == 0)
		numPieces++;
	else if(parsed >= 0 && (parsed & (static_cast<Int64>(1) << (numPieces * 7 - 1))) != 0)
		numPieces++;

	for(int i=0;i<numPieces;i++)
	{
		UInt8 encoded = static_cast<UInt8>((parsed >> (numPieces - i - 1)*7) & 0x7f);
		if(i != numPieces - 1)
			encoded |= 0x80;

		sprintf(numberSequence + strlen(numberSequence), "%i, ", encoded);
	}

	lua_pushstring(L, numberSequence);
	lua_pushinteger(L, numPieces);
	return 2;
}

// (type, value, signal)
// returns opcode, int1, int2, res1, str1
int encodeConstant(lua_State *L)
{
	const char *signal = lua_tostring(L, 3);
	if(!strcmp(signal, "Resource"))
	{
		lua_pushstring(L, "res");
		lua_pushnil(L);
		lua_pushnil(L);
		lua_pushvalue(L, 2);
		lua_pushnil(L);
		return 5;
	}

	if(!strcmp(signal, "Enum"))
	{
		// Just reuse the int value
		lua_pushstring(L, "constant");
		lua_pushvalue(L, 2);
		lua_pushnil(L);
		lua_pushvalue(L, 1);
		lua_pushnil(L);
		return 5;
	}

	if(!strcmp(signal, "NullRef"))
	{
		lua_pushstring(L, "null");
		lua_pushnil(L);
		lua_pushnil(L);
		lua_pushnil(L);
		lua_pushnil(L);
		return 5;
	}

	if(strcmp(signal, "Value"))
	{
		lua_pushstring(L, "Unknown signal for constant encoding");
		lua_error(L);
	}

	const char *coreType = lua_tostring(L, 1);

	if(!strcmp(coreType, "Core.int") || !strcmp(coreType, "Core.byte") || !strcmp(coreType, "Core.largeint"))
	{
		// Just reuse the int value
		lua_pushstring(L, "constant");
		lua_pushvalue(L, 2);
		lua_pushnil(L);
		lua_pushstring(L, coreType);
		lua_pushnil(L);
		return 5;
	}
	else if (!strcmp(coreType, "Core.string"))
	{
		// Store as a string
		lua_pushstring(L, "constant_str");
		lua_pushnil(L);
		lua_pushnil(L);
		lua_pushstring(L, coreType);
		lua_pushvalue(L, 2);
		return 5;
	}
	else if (!strcmp(coreType, "Core.float"))
	{
		return encodeFloat<Float>(coreType, L);
	}
	else if (!strcmp(coreType, "Core.double"))
	{
		return encodeFloat<Double>(coreType, L);
	}
	else if (!strcmp(coreType, "Core.bool"))
	{
		const char *str = lua_tostring(L, 2);
		lua_pushstring(L, "constant");
		if(!strcmp(str, "false"))
			lua_pushstring(L, "0");
		else
			lua_pushstring(L, "1");
		lua_pushnil(L);
		lua_pushstring(L, coreType);
		lua_pushnil(L);
		return 5;
	}

	lua_pushstring(L, "Invalid constant for encoding");
	lua_error(L);

	return 0;
}


int mkdirFunc(lua_State *L)
{
	_mkdir(lua_tostring(L, 1));
	return 0;
}

class lexState
{
	int _eofFlag;
	lua_Integer _lineNumber;
	lua_Integer _offset;
	lua_Integer _max;
	const UInt8 *_str;
	const char *_errorMessage;
	const char *_returnStr;
	size_t _returnStrSize;
	char *_returnStrBuf;
	size_t _returnStrCapacity;
	const char *_tokenType;

public:
	lexState(int eofFlag, lua_Integer lineNumber, lua_Integer offset, lua_Integer max, const char *str);
	~lexState();
	
	void NextChar();
	UInt8 Peek1();
	bool Peek2(UInt8 &out);
	bool Check2(UInt8 b1, UInt8 b2);
	void LexSingle();
	bool AppendChar(UInt8 c);

	static bool IsDigit(UInt8 c);
	static bool IsNameStartChar(UInt8 c);
	static bool IsNameChar(UInt8 c);

	bool MatchPunctuation();
	int ExportState(lua_State *L);
};

lexState::lexState(int eofFlag, lua_Integer lineNumber, lua_Integer offset, lua_Integer max, const char *str)
{
	_eofFlag = eofFlag;
	_lineNumber = lineNumber;
	_offset = offset;
	_max = max;
	_str = reinterpret_cast<const UInt8 *>(str);
	_errorMessage = NULL;
	_returnStr = NULL;
	_returnStrBuf = NULL;
	_returnStrSize = 0;
	_returnStrCapacity = 0;
	_tokenType = NULL;
}

lexState::~lexState()
{
	if(_returnStrBuf)
		realloc(_returnStrBuf, 0);
}

void lexState::NextChar()
{
	bool wasCR = false;
	UInt8 thisC = _str[_offset - 1];
	if(thisC == '\n')
		_lineNumber++;
	if(thisC == '\r')
	{
		_lineNumber++;
		wasCR = true;
	}
	_offset++;
	if(_offset > _max)
		_eofFlag = 1;

	if(wasCR && !_eofFlag && _str[_offset - 1] == '\n')
		_offset++;	// Windows CR/LF line break
}

UInt8 lexState::Peek1()
{
	return _str[_offset - 1];
}

bool lexState::Peek2(UInt8 &out)
{
	if(_offset >= _max)
		return false;
	out = _str[_offset];
	return true;
}

bool lexState::Check2(UInt8 b1, UInt8 b2)
{
	if(_offset == _max)
		return false;
	return (_str[_offset - 1] == b1) && (_str[_offset] == b2);
}

bool lexState::AppendChar(UInt8 c)
{
	if(_returnStrSize == _returnStrCapacity)
	{
		_returnStrCapacity *= 2;
		if(_returnStrCapacity == 0)
			_returnStrCapacity = 16;
		if(_returnStrCapacity > RDX_LARGEINT_MAX)
			return false;
		void *newBuf = realloc(_returnStrBuf, _returnStrCapacity);
		if(!newBuf)
			return false;
		_returnStrBuf = static_cast<char *>(newBuf);
		_returnStr = _returnStrBuf;
	}
	_returnStrBuf[_returnStrSize++] = static_cast<char>(c);
	return true;
}

bool lexState::IsDigit(UInt8 c)
{
	return (c >= '0' && c <= '9');
}

bool lexState::IsNameStartChar(UInt8 c)
{
	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}

bool lexState::IsNameChar(UInt8 c)
{
	return IsNameStartChar(c) || IsDigit(c);
}

bool lexState::MatchPunctuation()
{
	const char *matchStr = NULL;
	UInt8 b1, b2;
	bool has2;
	b1 = Peek1();
	has2 = Peek2(b2);
	switch(b1)
	{
	case ':':
		if(has2 && b2 == '<')
		{
			matchStr = ":<";
			break;
		}
		matchStr = ":";
		break;
	case '<':
		if(has2 && b2 == '=')
		{
			matchStr = "<=";
			break;
		}
		matchStr = "<";
		break;
	case '>':
		if(has2 && b2 == '=')
		{
			matchStr = ">=";
			break;
		}
		matchStr = ">";
		break;
	case '=':
		if(has2 && b2 == '=')
		{
			matchStr = "==";
			break;
		}
		matchStr = "=";
		break;
	case '!':
		if(has2 && b2 == '=')
		{
			matchStr = "!=";
			break;
		}
		matchStr = "!";
		break;
	case '+':
		if(has2)
		{
			if(b2 == '=')
			{
				matchStr = "+=";
				break;
			}
			if(b2 == '+')
			{
				matchStr = "++";
				break;
			}
		}
		matchStr = "+";
		break;
	case '-':
		if(has2)
		{
			if(b2 == '=')
			{
				matchStr = "-=";
				break;
			}
			if(b2 == '-')
			{
				matchStr = "--";
				break;
			}
		}
		matchStr = "-";
		break;
	case '*':
		if(has2 && b2 == '=')
		{
			matchStr = "*=";
			break;
		}
		matchStr = "*";
		break;
	case '/':
		if(has2 && b2 == '=')
		{
			matchStr = "/=";
			break;
		}
		matchStr = "/";
		break;
	case '&':
		if(has2 && b2 == '&')
		{
			matchStr = "&&";
			break;
		}
		matchStr = "&";
		break;
	case '|':
		if(has2 && b2 == '|')
		{
			matchStr = "||";
			break;
		}
		matchStr = "|";
		break;
	case ';': matchStr = ";"; break;
	case ',': matchStr = ","; break;
	case '{': matchStr = "{"; break;
	case '}': matchStr = "}"; break;
	case '[': matchStr = "["; break;
	case ']': matchStr = "]"; break;
	case '(': matchStr = "("; break;
	case ')': matchStr = ")"; break;
	case '.': matchStr = "."; break;
	case '?': matchStr = "?"; break;
	}

	if(matchStr)
	{
		_returnStr = matchStr;
		_returnStrSize = 0;
		while(*matchStr)
		{
			NextChar();
			_returnStrSize++;
			matchStr++;
		}
		_tokenType = "Punctuation";
		return true;
	}

	return false;
}

void lexState::LexSingle()
{
	bool moreWS = true;
	while(moreWS)
	{
		// Skip over whitespace
		UInt8 wsByte = ' ';

		while(Peek1() <= wsByte)
		{
			NextChar();
			if(_eofFlag)
				return;
		}

		moreWS = false;

		if(Check2('/', '/'))
		{
			UInt8 b = Peek1();
			while(b != '\n' && b != '\r')
			{
				NextChar();
				if(_eofFlag)
					return;
				b = Peek1();
			}
			moreWS = true;
		}

		if(Check2('/', '*'))
		{
			NextChar();
			NextChar();
			while(!Check2('*', '/'))
			{
				NextChar();
				if(_eofFlag)
				{
					_errorMessage = "UnexpectedEOF";
					return;
				}
			}
			NextChar();
			NextChar();
			moreWS = true;
		}

		if(!moreWS)
			break;
	}

	// Long strings
	if(Peek1() == '\"')
	{
		while(true)
		{
			NextChar();
			if(_eofFlag)
			{
				_errorMessage = "UnexpectedEOF";
				return;
			}
			UInt8 c = Peek1();
			if(c == '\n' || c == '\r')
			{
				_errorMessage = "NewlineInStringConstant";
				return;
			}
			if(c == '\"')
				break;
			if(c == '\\')
			{
				NextChar();
				if(_eofFlag)
				{
					_errorMessage = "UnexpectedEOF";
					return;
				}

				UInt8 escapeC = Peek1();
				switch(escapeC)
				{
				case 'n':
					c = '\n';
					break;
				case 'r':
					c = '\r';
					break;
				case 't':
					c = '\t';
					break;
				case '\"':
					c = '\"';
					break;
				case '\\':
					c = '\\';
					break;
				default:
					{
						_errorMessage = "UnknownEscape";
						return;
					}
				}
			}
			if(!AppendChar(c))
			{
				_errorMessage = "MemoryAllocationFailure";
				return;
			}
		}
		NextChar();
		_tokenType = "String";
		return;
	}

	if(IsDigit(Peek1()))
	{
		bool printNum = false;
		lua_Integer strStart = _offset;
		lua_Integer strEnd = _offset;
		while(true)
		{
			NextChar();
			if(_eofFlag)
				break;
			UInt8 c = Peek1();
			if(!IsDigit(c) && c != '.' && c != 'f' && c != '^')
				break;
			if(c == '^')
			{
				if(Check2('^', '-'))
				{
					strEnd = strEnd + 1;
					NextChar();	// Skip one
					c = Peek1();
					printNum = true;
				}
			}
			strEnd++;
		}
		_tokenType = "Number";
		_returnStr = reinterpret_cast<const char *>(_str + strStart - 1);
		_returnStrSize = strEnd - strStart + 1;
		return;
	}

	if(IsNameStartChar(Peek1()))
	{
		lua_Integer strStart = _offset;
		lua_Integer strEnd = _offset;

		while(true)
		{
			NextChar();
			if(_eofFlag)
				break;
			UInt8 c = Peek1();
			if(!IsNameChar(c))
				break;
			strEnd++;
		}
		
		_returnStr = reinterpret_cast<const char *>(_str + strStart - 1);
		_returnStrSize = strEnd - strStart + 1;
		_tokenType = "Name";
		return;
	}

	if(MatchPunctuation())
		return;

	_errorMessage = "UnknownSymbol";
}

// In state: eof, lineNumber, offset, max, str
// Out state: eof, lineNumber, offset, error message, token type, token text
int lexState::ExportState(lua_State *L)
{
	lua_pushboolean(L, _eofFlag);
	lua_pushinteger(L, _lineNumber);
	lua_pushinteger(L, _offset);
	if(_errorMessage) lua_pushstring(L, _errorMessage); else lua_pushnil(L);
	if(_tokenType) lua_pushstring(L, _tokenType); else lua_pushnil(L);
	if(_returnStr) lua_pushlstring(L, _returnStr, _returnStrSize); else lua_pushnil(L);
	return 6;
}


int lexSingleToken(lua_State *L)
{
	int eofFlag = lua_toboolean(L, 1);
	lua_Integer lineNumber = lua_tointeger(L, 2);
	lua_Integer offset = lua_tointeger(L, 3);
	lua_Integer max = lua_tointeger(L, 4);
	const char *str = NULL;
	if(!lua_isnoneornil(L, 5)) str = lua_tostring(L, 5);

	lexState ls(eofFlag, lineNumber, offset, max, str);
	ls.LexSingle();
	return ls.ExportState(L);
}


int msecTime(lua_State *L)
{
	lua_pushinteger(L, static_cast<lua_Integer>(MSecTime()));
	return 1;
}

extern "C" __declspec(dllexport) int RegisterRDXC(lua_State *L)
{
	lua_createtable(L, 0, 0);
	lua_createtable(L, 0, 0);
	lua_pushcfunction(L, parseNumber);
	lua_setfield(L, -2, "parseNumber");

	lua_pushcfunction(L, encodeConstant);
	lua_setfield(L, -2, "encodeConstant");

	lua_pushcfunction(L, encodeBytecodeInteger);
	lua_setfield(L, -2, "encodeBytecodeInteger");

	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpAdd<Int, Int>, false >));
	lua_setfield(L, -2, "cf_Core.int/methods/__add(Core.int)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpSubtract<Int, Int>, false >));
	lua_setfield(L, -2, "cf_Core.int/methods/__sub(Core.int)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpMultiply<Int, Int>, false >));
	lua_setfield(L, -2, "cf_Core.int/methods/__mul(Core.int)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpDivide<Int, Int>, true >));
	lua_setfield(L, -2, "cf_Core.int/methods/__div(Core.int)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpModulo<Int, Int>, true >));
	lua_setfield(L, -2, "cf_Core.int/methods/__mod(Core.int)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpGT<Int, bool>, false >));
	lua_setfield(L, -2, "cf_Core.int/methods/__gt(Core.int)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpLT<Int, bool>, false >));
	lua_setfield(L, -2, "cf_Core.int/methods/__lt(Core.int)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpGE<Int, bool>, false >));
	lua_setfield(L, -2, "cf_Core.int/methods/__ge(Core.int)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpLE<Int, bool>, false >));
	lua_setfield(L, -2, "cf_Core.int/methods/__le(Core.int)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpEQ<Int, bool>, false >));
	lua_setfield(L, -2, "cf_Core.int/methods/__eq(Core.int)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpNE<Int, bool>, false >));
	lua_setfield(L, -2, "cf_Core.int/methods/__ne(Core.int)");
	
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpAdd<Double, Double>, false >));
	lua_setfield(L, -2, "cf_Core.double/methods/__add(Core.double)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpSubtract<Double, Double>, false >));
	lua_setfield(L, -2, "cf_Core.double/methods/__sub(Core.double)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpMultiply<Double, Double>, false >));
	lua_setfield(L, -2, "cf_Core.double/methods/__mul(Core.double)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpDivide<Double, Double>, false >));
	lua_setfield(L, -2, "cf_Core.double/methods/__div(Core.double)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpGT<Double, bool>, false >));
	lua_setfield(L, -2, "cf_Core.double/methods/__gt(Core.double)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpLT<Double, bool>, false >));
	lua_setfield(L, -2, "cf_Core.double/methods/__lt(Core.double)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpGE<Double, bool>, false >));
	lua_setfield(L, -2, "cf_Core.double/methods/__ge(Core.double)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpLE<Double, bool>, false >));
	lua_setfield(L, -2, "cf_Core.double/methods/__le(Core.double)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpEQ<Double, bool>, false >));
	lua_setfield(L, -2, "cf_Core.double/methods/__eq(Core.double)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpNE<Double, bool>, false >));
	lua_setfield(L, -2, "cf_Core.double/methods/__ne(Core.double)");

	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpAdd<Float, Float>, false >));
	lua_setfield(L, -2, "cf_Core.float/methods/__add(Core.float)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpSubtract<Float, Float>, false >));
	lua_setfield(L, -2, "cf_Core.float/methods/__sub(Core.float)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpMultiply<Float, Float>, false >));
	lua_setfield(L, -2, "cf_Core.float/methods/__mul(Core.float)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpDivide<Float, Float>, false >));
	lua_setfield(L, -2, "cf_Core.float/methods/__div(Core.float)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpGT<Float, bool>, false >));
	lua_setfield(L, -2, "cf_Core.float/methods/__gt(Core.float)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpLT<Float, bool>, false >));
	lua_setfield(L, -2, "cf_Core.float/methods/__lt(Core.float)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpGE<Float, bool>, false >));
	lua_setfield(L, -2, "cf_Core.float/methods/__ge(Core.float)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpLE<Float, bool>, false >));
	lua_setfield(L, -2, "cf_Core.float/methods/__le(Core.float)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpEQ<Float, bool>, false >));
	lua_setfield(L, -2, "cf_Core.float/methods/__eq(Core.float)");
	lua_pushcfunction(L, (ConstFoldBinaryOp<BinOpNE<Float, bool>, false >));
	lua_setfield(L, -2, "cf_Core.float/methods/__ne(Core.float)");

	lua_pushcfunction(L, ConstFoldUnaryOp<UnOpNegate<Int> >);
	lua_setfield(L, -2, "cf_Core.int/methods/__neg()");
	lua_pushcfunction(L, ConstFoldUnaryOp<UnOpNegate<Float> >);
	lua_setfield(L, -2, "cf_Core.float/methods/__neg()");
	lua_pushcfunction(L, ConstFoldUnaryOp<UnOpNegate<Double> >);
	lua_setfield(L, -2, "cf_Core.double/methods/__neg()");

	lua_pushcfunction(L, (ConstFoldConvertOp<Int, Float>));
	lua_setfield(L, -2, "cf_Core.int/methods/#coerce(Core.float)");
	
	lua_pushcfunction(L, (ConstFoldConvertOp<Double, Float>));
	lua_setfield(L, -2, "cf_Core.double/methods/#coerce(Core.float)");
	lua_pushcfunction(L, (ConstFoldConvertOp<Double, Int>));
	lua_setfield(L, -2, "cf_Core.double/methods/#coerce(Core.int)");

	lua_pushcfunction(L, (ConstFoldConvertOp<Float, Double>));
	lua_setfield(L, -2, "cf_Core.float/methods/#coerce(Core.double)");
	lua_pushcfunction(L, (ConstFoldConvertOp<Float, Int>));
	lua_setfield(L, -2, "cf_Core.float/methods/#coerce(Core.int)");

	lua_pushcfunction(L, (ConstFoldConvertOp<Int, LargeInt>));
	lua_setfield(L, -2, "cf_Core.int/methods/#coerce(Core.largeint)");
	lua_pushcfunction(L, (ConstFoldConvertOp<Int, UShort>));
	lua_setfield(L, -2, "cf_Core.int/methods/#coerce(Core.ushort)");
	lua_pushcfunction(L, (ConstFoldConvertOp<Int, Byte>));
	lua_setfield(L, -2, "cf_Core.int/methods/#coerce(Core.byte)");
	lua_pushcfunction(L, (IntToStringOp<Int>));
	lua_setfield(L, -2, "cf_Core.int/methods/#coerce(Core.string)");

	lua_pushcfunction(L, (ConstFoldConvertOp<Short, Int>));
	lua_setfield(L, -2, "cf_Core.short/methods/#coerce(Core.int)");

	// String-to-number ops should just be handled as const fold converts to the same type
	lua_pushcfunction(L, (ConstFoldConvertOp<Int, Int>));
	lua_setfield(L, -2, "cf_Core.string/methods/#coerce(Core.int)");

	lua_pushcfunction(L, sha256);
	lua_setfield(L, -2, "sha256");
	lua_pushcfunction(L, crc32);
	lua_setfield(L, -2, "crc32");

	
	lua_pushcfunction(L, closeCacheFile);
	lua_setfield(L, -2, "closeCacheFile");
	lua_pushcfunction(L, writeCacheObject);
	lua_setfield(L, -2, "writeCacheObject");
	lua_pushcfunction(L, createCacheFile);
	lua_setfield(L, -2, "createCacheFile");
	lua_pushcfunction(L, readCacheObject);
	lua_setfield(L, -2, "readCacheObject");
	lua_pushcfunction(L, mkdirFunc);
	lua_setfield(L, -2, "mkdir");
	lua_pushcfunction(L, lexSingleToken);
	lua_setfield(L, -2, "lexSingleToken");
	
	lua_pushcfunction(L, msecTime);
	lua_setfield(L, -2, "msecTime");

	
	lua_setfield(L, -2, "Native");
	lua_setglobal(L, "RDXC");

	return 0;
}
